module aru_arb_wrgen_crd_gen (
    input logic                           clk,
    input logic                           rst_n,
          aru_arb_wrgen_cfg_if.crd_gen_in u_aru_cfg_if,
          aru_idx_if.out                  u_aru_idx_if,
          aru_sdb_if.out                  u_aru_sdb_if
);

    localparam P_LOOP = `M0 / `P_ARU;

    logic cfg_vld;
    logic lst_req_in_instr;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cfg_vld <= 'd0;
        end else if (cfg_vld) begin
            if (u_aru_idx_if.vld && u_aru_idx_if.rdy && lst_req_in_instr) begin
                cfg_vld <= 'd0;
            end
        end else begin
            if (u_aru_cfg_if.vld) begin
                cfg_vld <= 1'b1;
            end
        end
    end

    idx_t slice_m1 = (u_aru_cfg_if.slice_m + `M0 - 1) / `M0;
    idx_t slice_n1 = (u_aru_cfg_if.slice_n + `N0 - 1) / `N0;
    idx_t m1_idx, n1_idx, m1_idx_next, n1_idx_next;
    logic [$clog2(P_LOOP)-1:0] p_idx, p_idx_next;

    logic p_inc, p_clr, n1_inc, n1_clr, m1_inc, m1_clr;
    always_comb begin
        if (u_aru_cfg_if.reduce_m) begin
            // Reduce M mode
            p_inc  = 1'b1;
            p_clr  = (p_idx + 'd1 == `M0 / `P_ARU) && p_inc;
            m1_inc = p_clr;
            m1_clr = (m1_idx + 'd1 == slice_m1) && m1_inc;
            n1_inc = m1_clr;
            n1_clr = (n1_idx + 'd1 == slice_n1) && n1_inc;
        end else if (u_aru_cfg_if.reduce_n) begin
            // Reduce N mode
            n1_inc = 1'b1;
            n1_clr = (n1_idx + 'd1 == slice_n1) && n1_inc;
            p_inc  = n1_clr;
            p_clr  = (p_idx + 'd1 == `M0 / `P_ARU) && p_inc;
            m1_inc = p_clr;
            m1_clr = (m1_idx + 'd1 == slice_m1) && m1_inc;
        end else if (u_aru_cfg_if.reduce_m && u_aru_cfg_if.reduce_n) begin
            // Reduce MN mode - single output
            p_inc  = 1'b1;
            p_clr  = (p_idx + 'd1 == `M0 / `P_ARU) && p_inc;
            m1_inc = p_clr;
            m1_clr = (m1_idx + 'd1 == slice_m1) && m1_inc;
            n1_inc = m1_clr;
            n1_clr = (n1_idx + 'd1 == slice_n1) && n1_inc;
        end else begin
            // Normal mode (no reduce)
            n1_inc = 1'b1;
            n1_clr = (n1_idx + 'd1 == slice_n1) && n1_inc;
            p_inc  = n1_clr;
            p_clr  = (p_idx + 'd1 == `M0 / `P_ARU) && p_inc;
            m1_inc = p_clr;
            m1_clr = (m1_idx + 'd1 == slice_m1) && m1_inc;
        end

        // Update next indices
        p_idx_next  = p_clr ? 'd0 : (p_inc ? p_idx + 'd1 : p_idx);
        n1_idx_next = n1_clr ? 'd0 : (n1_inc ? n1_idx + 'd1 : n1_idx);
        m1_idx_next = m1_clr ? 'd0 : (m1_inc ? m1_idx + 'd1 : m1_idx);
    end

    // Detect last request in instruction
    always_comb begin
        if (u_aru_cfg_if.reduce_m && u_aru_cfg_if.reduce_n) begin
            // For reduce_mn, just one output
            lst_req_in_instr = 1'b1;
        end else if (u_aru_cfg_if.reduce_m) begin
            // For reduce_m
            lst_req_in_instr = (m1_idx == slice_m1 - 1) && (p_idx == P_LOOP - 1) && (n1_idx == slice_n1 - 1);
        end else if (u_aru_cfg_if.reduce_n) begin
            // For reduce_n
            lst_req_in_instr = (n1_idx == slice_n1 - 1) && (p_idx == P_LOOP - 1) && (m1_idx == slice_m1 - 1);
        end else begin
            // Normal mode
            lst_req_in_instr = (m1_idx == slice_m1 - 1) && (p_idx == P_LOOP - 1) && (n1_idx == slice_n1 - 1);
        end
    end

    // Update indices
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            p_idx  <= 'd0;
            n1_idx <= 'd0;
            m1_idx <= 'd0;
        end else if (u_aru_idx_if.vld && u_aru_idx_if.rdy && u_aru_cfg_if.vld) begin
            p_idx  <= p_idx_next;
            n1_idx <= n1_idx_next;
            m1_idx <= m1_idx_next;
        end
    end

    // Calculate valid dimensions and end flags
    logic eon, eom;
    logic [$clog2(`N0)-1:0] vld_n;
    logic [$clog2(`M0)-1:0] vld_m;

    always_comb begin
        if (u_aru_cfg_if.reduce_m && u_aru_cfg_if.reduce_n) begin
            // Reduce MN mode
            eon   = 1'b1;
            eom   = 1'b1;
            vld_n = `N0;  // Full width for reduce
            vld_m = `M0;  // Full height for reduce
        end else if (u_aru_cfg_if.reduce_m) begin
            // Reduce M mode
            eon   = (n1_idx == slice_n1 - 1);
            eom   = (m1_idx == slice_m1 - 1) && (p_idx == P_LOOP - 1);
            vld_n = (n1_idx == slice_n1 - 1) ? (u_aru_cfg_if.slice_n - n1_idx * `N0) : `N0;
            vld_m = (m1_idx == slice_m1 - 1) ? (u_aru_cfg_if.slice_m - m1_idx * `M0 - p_idx * `P_ARU) : `M0;
        end else if (u_aru_cfg_if.reduce_n) begin
            // Reduce N mode
            eon   = (n1_idx == slice_n1 - 1);
            eom   = (m1_idx == slice_m1 - 1);
            vld_n = (n1_idx == slice_n1 - 1) ? (u_aru_cfg_if.slice_n - n1_idx * `N0) : `N0;
            vld_m = (m1_idx == slice_m1 - 1) ? (u_aru_cfg_if.slice_m - m1_idx * `M0) : `M0;
        end else begin
            // Normal mode
            eon   = (n1_idx == slice_n1 - 1);
            eom   = (m1_idx == slice_m1 - 1) && (p_idx == P_LOOP - 1);
            vld_n = (n1_idx == slice_n1 - 1) ? (u_aru_cfg_if.slice_n - n1_idx * `N0) : `N0;
            vld_m = (m1_idx == slice_m1 - 1) ? (u_aru_cfg_if.slice_m - m1_idx * `M0 - p_idx * `P_ARU) : `M0;
        end
    end

    // Index assignments
    assign u_aru_idx_if.vld        = cfg_vld;
    assign u_aru_idx_if.pld.p_idx  = p_idx;
    assign u_aru_idx_if.pld.m1_idx = m1_idx;
    assign u_aru_idx_if.pld.n1_idx = n1_idx;

    // SDB assignments
    assign u_aru_sdb_if.vld        = cfg_vld;
    assign u_aru_sdb_if.pld.vld_m  = vld_m;
    assign u_aru_sdb_if.pld.vld_n  = vld_n;
    assign u_aru_sdb_if.pld.eom    = eom;
    assign u_aru_sdb_if.pld.eon    = eon;

    assign u_aru_cfg_if.rdy        = ~cfg_vld;

endmodule
